package utils.xtask;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import play.Logger;
import play.Play;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import utils.ExceptionUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhangzc
 * @since 5/18/18
 */
public class XTaskService {

    static ExecutorService threadPool = null;
    static AtomicLong increaseIndex = new AtomicLong();
    static JedisPool jedisPool;
    static int jedisDb = 0;

    private static String id(){
        return String.format("%s%04d", System.currentTimeMillis(), increaseIndex.incrementAndGet() % 10000);
    }

    public static void setServer(String url){
        setServer(url, null);
    }

    public static void setServer(String url, String password){
        if("mock".equalsIgnoreCase(url)){
            jedisPool = null;
            return;
        }
        JedisPoolConfig config = new JedisPoolConfig();
        //配置最大jedis实例数
        config.setMaxTotal(1000);
        //配置资源池最大闲置数
        config.setMaxIdle(200);
        //等待可用连接的最大时间
        config.setMaxWaitMillis(10000);
        //在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的
        config.setTestOnBorrow(true);

        Pattern pattern = Pattern.compile("redis://([a-zA-Z\\d.]+):([\\d]+)/([\\d]+)");
        Matcher matcher = pattern.matcher(url);
        if(matcher.find()){
            String ip = matcher.group(1);
            int port = Integer.parseInt(matcher.group(2));
            jedisDb = Integer.parseInt(matcher.group(3));
            jedisPool = new JedisPool(config, ip,port, 3000, password, jedisDb);
            threadPool = Executors.newSingleThreadExecutor();
        }

    }

    public static void shutdown() {
        if (threadPool != null) {
            threadPool.shutdown();
        }

    }

    public static void check(){
        if(jedisPool == null || threadPool == null){
            String url = Play.configuration.getProperty("xtask.url", "mock");
            String password = Play.configuration.getProperty("xtask.password");
            setServer(url, password);
        }
    }

    public static void saveTime(String jobName, String type, long time){
        check();
        if(jedisPool == null || threadPool == null){
            return ;
        }

        final String thJobName = StringUtils.replace(jobName,".", "_");

        //启动一个线程来发送，避免线程阻塞
        threadPool.execute(()->{
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.select(jedisDb);
                jedis.hset("TaskTime-"+thJobName, type, time+"");
            }catch (Exception ex){
                Logger.error(ex, "Error in XTaskService.saveTime()");
            }finally {
                jedisPool.returnResource(jedis);
            }
        });
    }

    public static long getTime(String jobName, String type){
        check();
        if(jedisPool == null || threadPool == null){
            return 0l;
        }

        final String thJobName = StringUtils.replace(jobName,".", "_");

        Jedis jedis = jedisPool.getResource();
        try{
            jedis.select(jedisDb);
            String timeStr = jedis.hget("TaskTime-"+thJobName, type);
            if(StringUtils.isEmpty(timeStr)){
                return 0l;
            }
            return Long.parseLong(timeStr);
        }catch (Exception ex){
            Logger.error(ex, "Error in XTaskService.getTime()");
            return 0l;
        }finally {
            jedisPool.returnResource(jedis);
        }
    }

    public static void saveExecuteCount(String jobName){
        check();
        if(jedisPool == null || threadPool == null){
            return ;
        }

        final String thJobName = StringUtils.replace(jobName,".", "_");

        //启动一个线程来发送，避免线程阻塞
        threadPool.execute(()->{
            Jedis jedis = jedisPool.getResource();
            try{
                jedis.select(jedisDb);
                jedis.incr("TaskExecuteCount-"+thJobName);
            }catch (Exception ex){
                Logger.error(ex, "Error in XTaskService.saveExecuteCount()");
            }finally {
                jedisPool.returnResource(jedis);
            }
        });
    }

    public static long getExecuteCount(String jobName){
        check();
        if(jedisPool == null || threadPool == null){
            return 0l;
        }

        final String thJobName = StringUtils.replace(jobName,".", "_");

        Jedis jedis = jedisPool.getResource();
        try{
            jedis.select(jedisDb);
            String countStr = jedis.get("TaskExecuteCount-"+thJobName);
            if(StringUtils.isEmpty(countStr)){
                return 0l;
            }
            return Long.parseLong(countStr);
        }catch (Exception ex){
            Logger.error(ex, "Error in XTaskService.getExecuteCount()");
            return 0l;
        }finally {
            jedisPool.returnResource(jedis);
        }
    }

    public static void saveLog(String jobName, String jobId, String logContent){
        saveLog(jobName, jobId, "INFO", logContent);
    }

    public static void saveLog(String jobName, String jobId, String type, String logContent){
        saveLog(jobName, jobId, type, logContent, null);
    }

    public static void saveLog(String jobName, String jobId, String type, String logContent, Throwable exception){
        check();
        if(jedisPool == null || threadPool == null){
            return ;
        }

        final String theJobName = StringUtils.replace(jobName,".", "_");

        Jedis jedis = jedisPool.getResource();
        try{
            jedis.select(jedisDb);
            String taskLogKey = "TaskLogs-"+theJobName + "-" + jobId;
            String logStr = jedis.get(taskLogKey);
            JSONObject logObj = null;
            long now = System.currentTimeMillis();
            if(StringUtils.isEmpty(logStr)){
                logObj = new JSONObject();
                logObj.put("progress", 0);
                logObj.put("startTime", now);
                logObj.put("endTime", 0);

                JSONArray logs = new JSONArray();
                logObj.put("logs", logs);
            }else{
                logObj = JSON.parseObject(logStr);
            }

            JSONArray logs = logObj.getJSONArray("logs");
            JSONObject logItem = new JSONObject();
            logItem.put("type", type);
            logItem.put("time", now);
            logItem.put("content", logContent);
            if(exception != null){
                logItem.put("exception", ExceptionUtil.getErrorInfoFromException(exception));
            }
            logs.add(logItem);

            jedis.set(taskLogKey, logObj.toJSONString());
            //7天后失效
            jedis.expire(taskLogKey, 7 * 24 * 60 * 60);

            jedis.zadd("TaskLogList-"+theJobName, now, jobId);

        }catch (Exception ex){
            Logger.error(ex, "Error in XTaskService.saveLog()");
        }finally {
            jedisPool.returnResource(jedis);
        }
    }

    public static List<JSONObject> getLogList(String jobName){
        check();
        if(jedisPool == null || threadPool == null){
            return Collections.emptyList();
        }

        final String theJobName = StringUtils.replace(jobName,".", "_");

        Jedis jedis = jedisPool.getResource();
        try{
            jedis.select(jedisDb);

            Set<String> jobSet = jedis.zrevrange("TaskLogList-"+theJobName, 0, -1);
            List<JSONObject> result = new ArrayList(jobSet.size());
            //最大数量
            final int Max = 100;
            for(String jobId: jobSet){
                String taskLogKey = "TaskLogs-"+theJobName + "-" + jobId;
                String log = jedis.get(taskLogKey);
                if(StringUtils.isNotEmpty(log)){
                    result.add(JSON.parseObject(log));
                    if(result.size() >= Max){  //限制条数，避免系统过载
                        break;
                    }
                }
            }

            return result;

        }catch (Exception ex){
            Logger.error(ex, "Error in XTaskService.saveLog()");
            return Collections.emptyList();
        }finally {
            jedisPool.returnResource(jedis);
        }
    }

    public static void saveLogProgress(String jobName, String jobId, int progress){
        check();
        if(jedisPool == null || threadPool == null){
            return ;
        }

        final String theJobName = StringUtils.replace(jobName,".", "_");

        Jedis jedis = jedisPool.getResource();
        try{
            jedis.select(jedisDb);
            String taskLogKey = "TaskLogs-"+theJobName + "-" + jobId;
            String logStr = jedis.get(taskLogKey);
            JSONObject logObj = null;
            long now = System.currentTimeMillis();
            if(StringUtils.isNotEmpty(logStr)){
                logObj = JSON.parseObject(logStr);
                logObj.put("progress", progress);
                if(progress >= 100){
                    logObj.put("endTime", System.currentTimeMillis());
                }
                jedis.set(taskLogKey, logObj.toJSONString());
                //7天后失效
                jedis.expire(taskLogKey, 7 * 24 * 60 * 60);
            }

        }catch (Exception ex){
            Logger.error(ex, "Error in XTaskService.saveLog()");
        }finally {
            jedisPool.returnResource(jedis);
        }
    }
}
